package com.gt.quality.base;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.ContentType;
import cn.idev.excel.FastExcel;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gt.quality.base.pojo.BaseLimitRequest;
import com.gt.quality.base.pojo.BaseLimitResponse;
import com.gt.quality.base.pojo.BasePage;
import com.gt.quality.base.pojo.LayerListViewObject;
import com.gt.quality.base.pojo.LayerTreeSelect;
import com.gt.quality.base.pojo.Resp;
import com.gt.quality.config.aspectj.annotation.Log;
import com.gt.quality.config.aspectj.enums.BusinessType;
import com.gt.quality.system.entity.SysFileInfo;
import com.gt.quality.system.entity.vo.MenuTreeVo;
import com.gt.quality.utils.CreateTreeUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author makeJava
 */
@Slf4j
@SuppressWarnings("unused")
public abstract class BaseCommonController<T extends Serializable> {

    public static final String XLSX = ".xlsx";

    public abstract BaseCommonService<T> getThisService();

    protected LayerListViewObject buildTree() {
        LayerListViewObject viewObject = new LayerListViewObject();
        List<T> list = getThisService().list(new QueryWrapper<>());
        List<MenuTreeVo> menuTreeVoList = new ArrayList<>(list.size());
        for (T item : list) {
            MenuTreeVo menuTreeVo = CreateTreeUtils.buildMenuTreeVo(this.getTreeId(item), this.getTreeParentId(item), this.getTreeShowName(item), "layui-icon-template");
            menuTreeVoList.add(menuTreeVo);
        }
        List<MenuTreeVo> allMenu = CreateTreeUtils.createAnyThingTree(menuTreeVoList);
        // 默认首节点选中
        if (CollUtil.isNotEmpty(allMenu)) {
            allMenu.getFirst().setChecked(true);
        }
        viewObject.setNextLayer(allMenu);
        viewObject.setCurrentLayer(null);
        return viewObject;
    }

    // list找出改节点的所有父级节点--
    protected List<LayerTreeSelect> queryToTree(List<T> list, Long id) {
        // 将祖先ID转换为Set以便快速查找
        java.util.Set<Long> ancestorIdSet = new java.util.HashSet<>();
        if (id != null) {
            // 查找指定部门的所有父级
            T targetData = list.stream().filter(item -> getTreeId(item).equals(id)).findFirst().orElse(null);
            if (targetData != null) {
                // 获取所有祖先ID
                String[] ancestorIds = this.getAncestors(targetData).split(",");
                for (String ancestorId : ancestorIds) {
                    if (!ancestorId.isEmpty()) {
                        try {
                            ancestorIdSet.add(Long.parseLong(ancestorId));
                        } catch (NumberFormatException ignored) {
                        }
                    }
                }
                // 添加目标部门本身
                ancestorIdSet.add(id);
            }
        }
        boolean hasDeptNeedOpen = !ancestorIdSet.isEmpty();
        // 封装成后续的返回值
        List<LayerTreeSelect> treeVoList = new ArrayList<>(list.size());
        for (T item : list) {
            LayerTreeSelect treeVo = CreateTreeUtils.buildLayerTreeSelect(getTreeId(item), getTreeParentId(item), getTreeShowName(item));
            // 过滤list，只保留目标部门及其祖先部门
            if (hasDeptNeedOpen && ancestorIdSet.contains(getTreeId(item))) {
                treeVo.setSpread(Boolean.TRUE);
            }
            treeVoList.add(treeVo);
        }
        return CreateTreeUtils.createLayerTreeSelect(treeVoList);
    }


    protected String getAncestors(T item) {
        throw new BusinessSelfException("数据异常,未找到节点的层级码");
    }

    /**
     * 获取树Id
     *
     * @return Long
     */
    protected Long getTreeId(T item) {
        throw new BusinessSelfException("数据异常,未找到节点的Id");
    }

    /**
     * 需要调用方具体实现
     * 获取树的父级Id
     *
     * @return Long
     */
    protected Long getTreeParentId(T item) {
        throw new BusinessSelfException("数据异常,未找到节点的父级Id");
    }


    /**
     * 需要调用方具体实现
     * 获取树的节点名称
     *
     * @return String
     */
    protected String getTreeShowName(T item) {
        throw new BusinessSelfException("数据异常,未找到节点的显示名称");
    }


    /**
     * 分页查询数据
     *
     * @param limitRequest 查询实体
     * @return 所有数据
     */
    @PostMapping("/queryLimit")
    @Operation(summary = "分页查询请求")
    public Resp<Object> queryPage(@RequestBody BaseLimitRequest<T> limitRequest) {
        // 先---执行扩展
        extendBeforeByQueryLimit(limitRequest);
        // 分页查询
        IPage<T> page = getThisService().queryLimitPage(limitRequest);
        // 封装返回结果集
        BaseLimitResponse<T> data = BaseLimitResponse.getInstance(page.getRecords(), page.getTotal(), page.getPages(), limitRequest.getPageIndex(), limitRequest.getPageSize());
        // 后---执行扩展||可以执行类型转化
        Object object = extendAfterByQueryLimit(data);
        return Resp.ok(object);
    }

    /**
     * 分页查询扩展点--前置
     */
    protected void extendBeforeByQueryLimit(BaseLimitRequest<T> limitRequest) {

    }


    /**
     * 分页查询扩展点---后置
     */
    protected Object extendAfterByQueryLimit(BaseLimitResponse<T> data) {

        return data;
    }

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @GetMapping("/queryOne/{id}")
    @Operation(summary = "查询ById")
    @Parameter(name = "id", description = "id", in = ParameterIn.PATH)
    public Resp<Object> selectOne(@PathVariable("id") Serializable id) {
        T oneBody = getThisService().getById(id);
        // 后置扩展--可能会有类型转化为前端view对象
        Object viewOjb = extendAfterByOne(oneBody);
        return Resp.ok(viewOjb);
    }

    /**
     * Id查询扩展点---后置
     */
    protected Object extendAfterByOne(T one) {
        return one;
    }


    /**
     * 新增数据
     *
     * @param obj 实体对象
     * @return 新增结果
     */
    @PostMapping("/save")
    @Log(title = "租户", businessType = BusinessType.INSERT)
    @Operation(summary = "新增数据")
    public Resp<String> insert(@RequestBody T obj) {
        // 新增--前置扩展
        extendBeforeBySave(obj);
        // 执行新增
        getThisService().save(obj);
        // 新增--后置扩展
        extendAfterBySave(obj);
        return Resp.ok("新增数据--成功");
    }

    // 新增前置扩展
    protected void extendBeforeBySave(T obj) {

    }

    // 新增后置扩展
    protected void extendAfterBySave(T obj) {

    }

    /**
     * 批量新增数据
     *
     * @param list 实体对象
     * @return 新增结果
     */
    @PostMapping("/saveBatch")
    @Log(title = "租户", businessType = BusinessType.INSERT)
    @Operation(summary = "批量新增数据")
    public Resp<String> insertBatch(@RequestBody List<T> list) {
        // 批量新增前置--扩展
        extendBeforeBySaveBatch(list);
        getThisService().saveBatchByEasyBaseMapper(list);
        return Resp.ok("批量新增数据--成功");
    }


    // 新增批量前置扩展
    protected void extendBeforeBySaveBatch(List<T> list) {
    }


    /**
     * 修改数据
     *
     * @param obj 实体对象
     * @return 修改结果
     */
    @PutMapping("/update")
    @Log(title = "租户", businessType = BusinessType.UPDATE)
    @Operation(summary = "修改数据")
    public Resp<String> update(@RequestBody T obj) {
        // 修改--前置扩展
        extendBeforeByUpdate(obj);
        // 执行修改
        getThisService().updateById(obj);
        // 修改--后置扩展
        extendAfterByUpdate(obj);
        return Resp.ok("修改数据--成功");
    }


    // 修改--前置扩展
    protected void extendBeforeByUpdate(T obj) {
    }

    // 修改--后置扩展
    protected void extendAfterByUpdate(T obj) {
    }

    /**
     * 删除数据
     *
     * @param idList 主键结合
     * @return 删除结果
     */
    @DeleteMapping("/dels")
    @Log(title = "租户", businessType = BusinessType.DELETE)
    @Operation(summary = "删除数据")
    public Resp<String> delete(@RequestParam("idList") List<Long> idList) {
        // 删除--前置扩展
        extendBeforeByDelete(idList);
        // 删除
        boolean delNumber = getThisService().removeByIds(idList);
        // 删除--后置扩展
        extendAfterByDelete(idList);
        String executeMsg = "idList 是：" + idList;
        return delNumber ? Resp.ok(executeMsg) : Resp.error(executeMsg);
    }

    // 删除--前置扩展
    protected void extendBeforeByDelete(List<Long> idList) {
    }

    // 删除--后置扩展
    protected void extendAfterByDelete(List<Long> idList) {
    }

    /**
     * 是否支持Excel导入
     */
    protected abstract boolean isSuperExcel();

    /**
     * Excel导入
     *
     * @return 导入结果
     */
    @PostMapping("/excelInput")
    @Log(title = "Excel导入通用", businessType = BusinessType.INSERT)
    @Operation(summary = "Excel导入")
    public Resp<SysFileInfo> excelInput(@RequestParam("file") MultipartFile multipartFile) {
        if (isSuperExcel()) {
            return getThisService().excelInput(multipartFile);
        }
        return Resp.error("不支持Excel导入");
    }

    /**
     * 导出JSON模板
     */
    @GetMapping("/export/json")
    @Operation(summary = "JSON导出通用")
    public void exportJson(HttpServletResponse response, T entity, BasePage pageQuery) throws IOException {
        // 原始表数据--完整版|不要替换为vo|dto|bo等非原始对象
        QueryWrapper<T> queryWrapper = getThisService().forQueryWhere(entity);
        IPage<T> page = new Page<>(pageQuery.getPageIndex(), pageQuery.getPageSize());
        List<T> dataList = getThisService().list(page, queryWrapper);
        // 使用ObjectMapper将对象转换成JSON格式的字节数组
        ObjectMapper objectMapper = new ObjectMapper();
        byte[] data = objectMapper.writeValueAsBytes(dataList);
        // 设置响应头信息
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment; filename=data.json");
        response.setContentLength(data.length);
        // 写出数据到响应体
        response.getOutputStream().write(data);
        response.getOutputStream().flush();
    }

    /**
     * 导入JSON模板
     */
    @PostMapping("/import/json")
    @Operation(summary = "JSON导入通用")
    public Resp<Boolean> importJson(@RequestParam("file") MultipartFile multipartFile) {
        // 检查上传的文件是否为空
        if (multipartFile.isEmpty()) {
            throw new BusinessSelfException("上传的文件不能为空");
        }
        if (!ContentType.JSON.getValue().equals(multipartFile.getContentType())) {
            throw new BusinessSelfException("上传的文件不是JSON格式的");
        }
        //  读取JSON文件里的内容到List
        try (InputStream inputStream = multipartFile.getInputStream()) {
            // 使用ObjectMapper解析JSON文件内容
            ObjectMapper objectMapper = new ObjectMapper();
            List<T> dataModels = objectMapper.readValue(inputStream,
                    objectMapper.getTypeFactory().constructCollectionType(List.class, getThisService().getEntityClass()));
            getThisService().saveBatch(dataModels);
        } catch (Exception e) {
            // 记录错误日志，并抛出自定义异常，指示用户导入失败
            log.error(e.getMessage(), e);
            throw new BusinessSelfException("导入失败,请联系管理员");
        }
        return Resp.ok(Boolean.TRUE);
    }


    /**
     * 文件下载并且失败的时候返回json（默认失败了会返回一个有部分数据的Excel）
     *
     * @since 2.1.1
     */
    @PostMapping("/excelOutput")
    @Operation(summary = "Excel导出通用")
    public void downloadFailedUsingJson(@RequestBody BaseLimitRequest<T> limitRequest, HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        try {
            if (!isSuperExcel()) {
                throw new BusinessSelfException("不支持Excel导入");
            }
            // 分页查询
            IPage<T> page = getThisService().queryLimitPage(limitRequest);
            response.setHeader("Content-Disposition", "attachment; filename=example.xlsx");
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode(DateUtil.format(new Date(), DatePattern.CHINESE_DATE_PATTERN) + "导出Excel", StandardCharsets.UTF_8).replace("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + XLSX);
            // 这里需要设置不关闭流
            FastExcel.write(response.getOutputStream(), limitRequest.getRequestBody().getClass()).autoCloseStream(Boolean.FALSE).sheet("数据")
                    .doWrite(page.getRecords());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Resp<Object> error = Resp.error("数据导出失败:" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(error));
        }
    }
}
